package com.piece.core.jpa.listener;

import com.piece.core.db.context.DynamicDataSourceContextHolder;
import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.basic.ConfigUtil;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.jpa.annotation.BeanModifyLog;
import com.piece.core.log.client.LogFactory;
import com.piece.core.log.client.BeanPostLogService;
import com.piece.core.log.client.dto.BeanPostLogDTO;
import com.piece.core.log.client.dto.BeanPostLogInfoDTO;
import com.piece.core.log.enums.BeanPostLogType;
import com.piece.core.log.support.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.event.spi.*;
import org.hibernate.persister.entity.EntityPersister;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
public class BeanPostLogListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener {

    private String applicationName;
    private ApplicationProperties applicationProperties;
    private BeanPostLogService beanPostLogService;

    public BeanPostLogListener(ApplicationProperties applicationProperties, LogFactory logFactory) {
        this.applicationName = ConfigUtil.getInstance().get("spring.application.name");
        this.applicationProperties = applicationProperties;
        this.beanPostLogService = logFactory.getBeanPostLog();
    }

    private boolean enable() {
        return Convert.toBool(applicationProperties.getValue(ApplicationConstants.SYSTEM_MODIFY_LOG_ENABLE), false);
    }

    @Override
    public void onPostInsert(PostInsertEvent event) {
        if (enable() && null != beanPostLogService) {
            try {
                if (event.getEntity() instanceof BeanModifyLog) {
                    Object entity = event.getEntity();
                    BeanPostLogDTO logDTO = LogUtil.buildPostLog(applicationName);
                    logDTO.setSource(DynamicDataSourceContextHolder.getDateSourceType());
                    logDTO.setTableName(entity.getClass().getSimpleName());
                    logDTO.setEntityId(Convert.toStr(event.getId(), ""));
                    logDTO.setType(BeanPostLogType.INSERT.getType().toString());
                    logDTO.setClassName("BeanPostLogListener");
                    logDTO.setMethodName("onPostInsert");
                    List<Map> diffRecord = BeanUtil.diffRecord(BeanUtil.newInstance(entity.getClass()), entity);
                    List<BeanPostLogInfoDTO> details = new ArrayList();
                    for (Map record : diffRecord) {
                        BeanPostLogInfoDTO dto = BeanPostLogInfoDTO.builder().build();
                        dto = BeanUtil.mapToBean(record, dto);
                        details.add(dto);
                    }
                    logDTO.setInfos(details);
                    logDTO.setOperaTime(new Date());
                    beanPostLogService.save(logDTO);
                }
            } catch (Exception e) {
                log.error("数据表插入操作日记记录失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public void onPostUpdate(PostUpdateEvent event) {
        if (enable() && null != this.beanPostLogService) {
            try {
                if (event.getEntity() instanceof BeanModifyLog) {
                    Object entity = event.getEntity();
                    BeanPostLogDTO logDTO = LogUtil.buildPostLog(applicationName);
                    logDTO.setSource(DynamicDataSourceContextHolder.getDateSourceType());
                    logDTO.setTableName(entity.getClass().getSimpleName());
                    logDTO.setEntityId(Convert.toStr(event.getId(), ""));
                    logDTO.setType(BeanPostLogType.UPDATE.getType().toString());
                    logDTO.setClassName("BeanPostLogListener");
                    logDTO.setMethodName("onPostUpdate");

                    List<BeanPostLogInfoDTO> details = new ArrayList<>();
                    int[] dirtyProperties = event.getDirtyProperties();
                    String[] names = event.getPersister().getPropertyNames();
                    Object[] oldState = event.getOldState();
                    Object[] state = event.getState();
                    for (int index : dirtyProperties) {
                        details.add(BeanPostLogInfoDTO.builder()
                                .filedName(names[index])
                                .oldValue(Convert.toStr(oldState[index], ""))
                                .newValue(Convert.toStr(state[index], ""))
                                .build());
                    }
                    logDTO.setInfos(details);
                    logDTO.setOperaTime(new Date());
                    beanPostLogService.save(logDTO);
                }
            } catch (Exception e) {
                log.error("数据表更新操作日记记录失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public void onPostDelete(PostDeleteEvent event) {
        if (enable() && null != beanPostLogService) {
            try {
                if (event.getEntity() instanceof BeanModifyLog) {
                    Object entity = event.getEntity();
                    BeanPostLogDTO logDTO = LogUtil.buildPostLog(applicationName);
                    logDTO.setSource(DynamicDataSourceContextHolder.getDateSourceType());
                    logDTO.setTableName(entity.getClass().getSimpleName());
                    logDTO.setEntityId(Convert.toStr(event.getId(), ""));
                    logDTO.setType(BeanPostLogType.DELETE.getType().toString());
                    logDTO.setClassName("BeanPostLogListener");
                    logDTO.setMethodName("onPostDelete");
                    logDTO.setOperaTime(new Date());
                    beanPostLogService.save(logDTO);
                }
            } catch (Exception e) {
                log.error("数据表删除操作日记记录失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public boolean requiresPostCommitHanding(EntityPersister entityPersister) {
        return false;
    }
}
